package hypeerweb;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import states.NodeState;

public class Connections {
	private Fold fold;
	private SurrogateFold surrogateFold;
	private InverseSurrogateFold inverseSurrogateFold;
	private List<Neighbor> neighborsSmallWithout;
	private List<Neighbor> neighborsSmallWith;
	private List<Neighbor> neighborsBig;
	private List<InverseSurrogateNeighbor> upPointers;
	private List<SurrogateNeighbor> downPointers;

	public Connections() {
		neighborsSmallWithout = new ArrayList<Neighbor>();
		neighborsSmallWith = new ArrayList<Neighbor>();
		neighborsBig = new ArrayList<Neighbor>();
		upPointers = new ArrayList<InverseSurrogateNeighbor>();
		downPointers = new ArrayList<SurrogateNeighbor>();
		surrogateFold = null;
		inverseSurrogateFold = null;
		fold = null;
	}

	public Connections(Node node) {
		neighborsSmallWithout = new ArrayList<Node>();
		neighborsSmallWith = new ArrayList<Node>();
		neighborsBig = new ArrayList<Node>();
		upPointers = new ArrayList<Node>();
		downPointers = new ArrayList<Node>();
		surrogateFold = Node.NULL_NODE;
		inverseSurrogateFold = Node.NULL_NODE;
		fold = node;
	}

	public Connections(Connections conn, Node parent) {
		// creates child of conn
		this();
		if (conn.inverseSurrogateFold == Node.NULL_NODE) {
			fold = conn.fold;
		} else {
			fold = conn.inverseSurrogateFold;
		}
		neighborsSmallWith.add(parent);
		neighborsSmallWithout.addAll(conn.upPointers);
		downPointers.addAll(conn.neighborsBig);
	}

	public Node getSmallestNeighborWithoutChild() {
		if (neighborsSmallWithout.isEmpty()) {
			return Node.NULL_NODE;
		}

		return neighborsSmallWithout.get(0);
	}

	public Node getSmallestNeighborWithChild() {
		if (neighborsSmallWith.isEmpty()) {
			return Node.NULL_NODE;
		}

		return neighborsSmallWith.get(0);
	}

	public Node getSmallestSurrNeighbor() {
		if (downPointers.isEmpty()) {
			return Node.NULL_NODE;
		}

		return downPointers.get(0);
	}

	public Node getLargestConnection() {
		// last element of bigger
		Node compNeighbor = Node.NULL_NODE;
		if (neighborsBig.size() > 0) {
			compNeighbor = neighborsBig.get(neighborsBig.size() - 1);
		}

		// last element of inverseSurrogateNeighbors
		Node compISN = Node.NULL_NODE;
		if (upPointers.size() > 0) {
			compISN = upPointers.get(upPointers.size() - 1);
		}
		// fold
		Node compFold;
		if (fold != null) {
			compFold = fold;
		} else {
			compFold = surrogateFold;
		}

		Node largest = compNeighbor;

		if (largest.compareTo(compISN) == -1) {
			largest = compISN;
		}
		if (largest.compareTo(compFold) == -1) {
			largest = compFold;
		}
		return largest;
	}

	public Node getLargestISN() {
		if (upPointers.isEmpty()) {
			return Node.NULL_NODE;
		}

		return upPointers.get(upPointers.size() - 1);
	}

	public List<Node> getNeighbors() {
		ArrayList<Node> allNeighbors = new ArrayList<Node>();
		allNeighbors.addAll(neighborsSmallWith);
		allNeighbors.addAll(neighborsSmallWithout);
		allNeighbors.addAll(neighborsBig);
		return allNeighbors;
	}

	public List<Node> getNeighborsSmallWith() {
		return neighborsSmallWith;
	}

	public List<Node> getNeighborsSmallWithout() {
		return neighborsSmallWithout;
	}

	public List<Node> getNeighborsBig() {
		return neighborsBig;
	}

	// to get Surrogate Neighbors
	public List<Node> getDownPointers() {
		return downPointers;
	}

	// to get Inverse Surrogate Neighbors
	public List<Node> getUpPointers() {
		return upPointers;
	}

	public void addNeighbor(Node neighbor, WebId id)
	// TODO what do we need to do with this method?
	{
		if (id.getValue() < neighbor.getId()) {
			if (!neighborsBig.contains(neighbor)) {
				neighborsBig.add(neighbor);
			}
		} else {
			if (!neighborsSmallWithout.contains(neighbor)) {
				neighborsSmallWithout.add(neighbor);
			}
		}

	}

	public void removeNeighbor(Node neighbor) {
		// removed if statements because remove() returns false if the object is
		// not in the list
		neighborsBig.remove(neighbor);
		neighborsSmallWith.remove(neighbor);
		neighborsSmallWithout.remove(neighbor);
	}

	public void addUpPointer(Node upPointer) {
		if (!upPointers.contains(upPointer)) {
			upPointers.add(upPointer);
		}
	}

	public void removeUpPointer(Node upPointer) {
		upPointers.remove(upPointer);
	}

	public void addDownPointer(Node downPointer) {
		if (!downPointers.contains(downPointer)) {
			downPointers.add(downPointer);
		}
	}

	public void removeDownPointer(Node downPointer) {
		downPointers.remove(downPointer);
	}

	public void setFold(Node newFold) {
		fold = newFold;
	}

	public void setSurrogateFold(Node newSurrogateFold) {
		surrogateFold = newSurrogateFold;
	}

	public void setInverseSurrogateFold(Node newInverseSurrogateFold) {
		inverseSurrogateFold = newInverseSurrogateFold;
	}

	public Node getFold() {
		return fold;
	}

	public Node getSurrogateFold() {
		return surrogateFold;
	}

	public Node getInverseSurrogateFold() {
		return inverseSurrogateFold;
	}

	public int getHeight() {
		int height = 0;

		height += neighborsBig.size();
		height += neighborsSmallWith.size();
		height += neighborsSmallWithout.size();

		height += downPointers.size();

		return height;
	}

	private Node getSmallestDownPointer() {
		if (downPointers.size() == 0) {
			return Node.NULL_NODE;
		} else {
			return downPointers.get(0);
		}
	}

	private Node getLargestUpPointer() {
		if (upPointers.size() == 0) {
			return Node.NULL_NODE;
		} else {
			return upPointers.get(upPointers.size() - 1);
		}
	}

	private Node getSmallestNeighborWithout() {
		return neighborsSmallWithout.get(0);
	}

	public void deleteUpPointers() {
		upPointers.clear();
	}

	// Moves neighbor to the correct list when it receives a child.
	public void receivedChild(Node parent) {
		if (neighborsSmallWithout.isEmpty()) {
			neighborsSmallWithout.addAll(neighborsSmallWith);
			neighborsSmallWith.clear();
		}

		if (neighborsSmallWithout.isEmpty()) {
			return;
		}

		Node n = neighborsSmallWithout.get(0);
		if (n.compareTo(parent) == 0) {
			neighborsSmallWithout.remove(n);
			neighborsSmallWith.add(n);
		}

	}
	
	//these three are redundant with setFold, etc
	public void updateInverseSurrogateFold(Node node) {
		inverseSurrogateFold = node;
	}

	public void updateSurrogateFold(Node node) {
		surrogateFold = node;
		
	}

	public void updateFold(Node node) {
		fold = node;
		
	}

	public void updateUpPointer(Node node) {
		int idx = upPointers.indexOf(node);
		
		//should never happen
		if(idx == -1)
			return;
		
		upPointers.set(idx, node);
	}
	
	public void updateDownPointer(Node node)
	{
		int idx = downPointers.indexOf(node);
		
		//should never happen
		if(idx == -1)
			return;
		
		downPointers.set(idx, node);
	}

	public void updateNeighbor(Node node) {
		int idx = neighborsSmallWith.indexOf(node);
		
		if(idx > 0)
		{
			neighborsSmallWith.set(idx, node);
			return;
		} 
		
		idx = neighborsSmallWithout.indexOf(node);
		
		if(idx > 0)
		{
			neighborsSmallWithout.set(idx, node);
			return;
		}
		
		idx = neighborsBig.indexOf(node);
		
		if(idx > 0)
		{
			neighborsBig.set(idx, node);
			return;
		}
	}

}
